package com.chuangjiangx.member.stored.ddd.domain.service;

import com.alibaba.fastjson.JSON;
import com.chuangjiangx.commons.JacksonUtils;
import com.chuangjiangx.commons.exception.BaseException;
import com.chuangjiangx.member.basic.ddd.domain.model.*;
import com.chuangjiangx.member.coupon.ddd.domain.model.MbrConfig;
import com.chuangjiangx.member.coupon.ddd.domain.repository.MbrConfigRepository;
import com.chuangjiangx.member.basic.ddd.domain.service.MemberMsgDomainService;
import com.chuangjiangx.member.basic.ddd.domain.service.MemberDomainService;
import com.chuangjiangx.member.basic.ddd.domain.service.MemberRedisDomainService;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderMirror;
import com.chuangjiangx.member.stored.ddd.domain.MbrRandomUtils;
import com.chuangjiangx.member.coupon.ddd.domain.model.MbrHasCouponId;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderStatus;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderType;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrStoredType;
import com.chuangjiangx.member.score.ddd.domain.model.MbrScoreType;
import com.chuangjiangx.domain.shared.model.PayEntry;
import com.chuangjiangx.member.coupon.ddd.domain.model.MbrHasCoupon;
import com.chuangjiangx.member.coupon.ddd.domain.repository.MbrHasCouponRepository;
import com.chuangjiangx.member.coupon.ddd.domain.service.CouponDomainService;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrder;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderRepository;
import com.chuangjiangx.member.score.ddd.domain.model.MbrScoreGrandTotalRule;
import com.chuangjiangx.member.score.ddd.domain.repository.MbrScoreGrandTotalRuleRepository;
import com.chuangjiangx.member.score.ddd.domain.model.MbrScoreStream;
import com.chuangjiangx.member.score.ddd.domain.repository.MbrScoreStreamRepository;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrStoredStream;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrStoredStreamRepository;
import com.chuangjiangx.member.stored.ddd.domain.event.MbrPaySuccessEvent;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderId;
import com.chuangjiangx.member.common.utils.MbrKeyManager;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * @author cj-ch
 * @date 2018/5/24 上午8:55
 */
@Slf4j(topic = "pay")
@Service
public class MbrConsumerService {

    private static final String CONSUMER_LOCK_PRE = "C_L_";

    @Autowired
    private MemberRedisDomainService memberRedisDomainService;
    @Autowired
    private MbrScoreStreamRepository mbrScoreStreamRepository;
    @Autowired
    private MbrStoredStreamRepository mbrStoredStreamRepository;
    @Autowired
    private MbrAccountRepository mbrAccountRepository;
    @Autowired
    private MemberRepository memberRepository;
    @Autowired
    private MbrOrderRepository mbrOrderRepository;
    @Autowired
    private MemberMsgDomainService memberMsgDomainService;
    @Autowired
    private MemberDomainService memberDomainService;
    @Autowired
    private MbrScoreGrandTotalRuleRepository mbrScoreGrandTotalRuleRepository;
    @Autowired
    private CouponDomainService couponDomainService;
    @Autowired
    private MbrHasCouponRepository mbrHasCouponRepository;
    @Autowired
    private MbrConfigRepository mbrConfigRepository;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ObjectMapper objectMapper;


    @Transactional(rollbackFor = Exception.class )
    public void service(MbrPaySuccessEvent event){
        String lockKey = null;
        if(!check(event)){
            return;
        }
        if(null == (lockKey = lock(event))){
            return;
        }
        final Optional<MbrPaySuccessEvent> optionalEvent = Optional.of(event);
        final Supplier<String> msgAddress = () -> optionalEvent.map(MbrPaySuccessEvent::getStoreAddress)
                .orElse(optionalEvent.map(MbrPaySuccessEvent::getMerchantAddress).orElse(""));

        if(!Objects.equals(PayEntry.MSCARDPAY,event.getPayEntry())){
            //非会员卡消费
            otherConsumer(event);
        } else {
            //会员卡消费
            memberCardConsumer(event, msgAddress);
        }
        memberRedisDomainService.delKey(lockKey);
    }

    /**
     * 非会员卡消费
     * @param event
     */
    protected void otherConsumer(MbrPaySuccessEvent event) {
        //普通消费
        Member member;
        MbrAccount mbrAccount;
        if(null != event.getMemberId()){
            // 查询会员信息
            member = memberRepository.fromId(new MemberId(event.getMemberId()));
            mbrAccount = mbrAccountRepository.fromMemberId(member.getId());
            if(null != event.getMbrHasCouponId()) {
                //卡券消费
                //非会员卡消费
                //使用了卡券,会员这边会预下单
                //可能某些情况下实际付款金额与卡券验券时会员这边的下单需要付款的实际金额有出入,暂不考虑
                MbrHasCoupon mbrHasCoupon = mbrHasCouponRepository.fromId(new MbrHasCouponId(event.getMbrHasCouponId()));
                if(null == mbrHasCoupon){
                    log.warn("优惠券不存在?,event:{}",event.toString());
                }
                MbrOrder mbrOrder = mbrOrderRepository.fromId(new MbrOrderId(event.getMbrOrderId()));
                mbrOrder.setPayEntry(event.getPayEntry());
                mbrOrder.setPayType(event.getPayType());
                mbrOrder.setPayTerminal(event.getPayTerminal());
                mbrOrder.setOrderPayNumber(event.getOrderPayNumber());
                mbrOrder.couponUseSuccess();

                mbrOrderRepository.update(mbrOrder);
                couponDomainService.confirmUseCoupon(mbrHasCoupon.getId());
            }else{
                // 识别出了会员身份,未使用卡券
                // 同步订单
                MbrOrder mbrOrder = new MbrOrder(
                        MbrRandomUtils.generateMbrOrderNumber(),
                        MbrOrderType.CONSUME,
                        new MerchantId(event.getMerchantId()),
                        event.getMerchantUserId(),
                        event.getStoreId(),
                        event.getStoreUserId(),
                        new MemberId(event.getMemberId()),
                        event.getAmount(),
                        BigDecimal.ZERO,
                        event.getAmount(),
                        BigDecimal.ZERO,
                        new Date(),
                        null,
                        null,
                        MbrOrderStatus.PAID,
                        event.getPayEntry(),
                        event.getPayType(),
                        "",
                        event.getPayTerminal(),
                        event.getOrderPayNumber(),
                        null
                );
                mbrOrderRepository.save(mbrOrder);
            }
            //查询商户积分累计规则
            grandScore(event, member, mbrAccount, null);

            //保存账户数据
            mbrAccountRepository.update(mbrAccount);
        }

    }

    /**
     * 会员卡消费
     * @param event
     * @param msgAddress
     *
     */
    protected void memberCardConsumer(MbrPaySuccessEvent event, Supplier<String> msgAddress) {
        Member member = memberRepository.fromId(new MemberId(event.getMemberId()));
        MbrAccount mbrAccount = mbrAccountRepository.fromMemberId(member.getId());

        MbrOrder mbrOrder = fetchMbrOrder(event);
        log.info(">>>>>>mbrOrder == null?{},{}" ,(mbrOrder == null),(mbrOrder == null ? "" : JSON.toJSONString(mbrOrder)));
        //记录储值流水
        MbrStoredStream mbrStoredStream = recordMbrStoredStream(member, mbrAccount, mbrOrder, event);
        grandScore(event, member, mbrAccount, mbrStoredStream);

        //判断是否使用了优惠券
        if(null != event.getMbrHasCouponId()){
            //消费时使用优惠券
            //会员卡消费
            couponDomainService.confirmUseCoupon(new MbrHasCouponId(event.getMbrHasCouponId()));
        }
        //保存账户数据
        mbrAccountRepository.update(mbrAccount);
    }

    private MbrOrder fetchMbrOrder(MbrPaySuccessEvent event) {
        MbrOrder mbrOrder;
        String json = (String) redisTemplate.opsForValue().get(MbrKeyManager.MBR_ORDER_ID_KEY + event.getMbrOrderId());
        if(StringUtils.isNotBlank(json)){
            MbrOrderMirror mirror = JacksonUtils.toObject(objectMapper, json, MbrOrderMirror.class);
            mbrOrder = mirror.convertToMbrOrder();
        } else {
            mbrOrder = mbrOrderRepository.fromId(new MbrOrderId(event.getMbrOrderId()));
        }
        if(mbrOrder == null){
            throw new BaseException("","获取订单信息失败!订单ID:" + event.getMbrOrderId());
        }
        return mbrOrder;
    }

    /**
     * 累计积分
     * @param event
     * @param member
     * @param mbrAccount
     * @param mbrStoredStream
     */
    private void grandScore(MbrPaySuccessEvent event, Member member, MbrAccount mbrAccount, MbrStoredStream mbrStoredStream) {
        //默认累计积分
        boolean needGrandScore = true;
        //判断商户是否开启储值卡消费累计积分(默认是开启的)
        if(Objects.equals(PayEntry.MSCARDPAY,event.getPayEntry())){
            MbrConfig config = mbrConfigRepository.findByMerchantId(member.getOperationInfo().getMerchantId());
            if(null != config){
                if(Objects.equals(config.getCardConsumerGrantScore(),0)){
                    //消费入口是会员卡并且配置了会员消费是否累计积分为否
                    needGrandScore = false;
                }
            }
        }
        if(needGrandScore){
            //查询商户积分累计规则
            MbrScoreGrandTotalRule mbrScoreGrandTotalRule = mbrScoreGrandTotalRuleRepository.fromMerchantId(member.getOperationInfo().getMerchantId());
            if(null != mbrScoreGrandTotalRule){
                //计算本次消费所产生的积分
                Long score = mbrScoreGrandTotalRule.calcObtainScore(event.getRealPayAmount());
                //记录积分流水
                recordMbrScoreStream(event, member, mbrAccount, mbrStoredStream, score);
            }
        }
    }

    /**
     * 前置条件检查
     * @param event
     * @return
     */
    private boolean check(MbrPaySuccessEvent event){
        if(BigDecimal.ZERO.compareTo(event.getRealPayAmount()) > 0){
            return false;
        }
        if(Objects.equals(PayEntry.MSCARDPAY,event.getPayEntry())){
            //会员卡消费
            //查询储值流水表 - 根据会员订单Id
            MbrStoredStream from = mbrStoredStreamRepository.from(event.getMbrOrderId(), MbrStoredType.CONSUMER);
            if(null != from){
                // 已经处理过
                return false;
            }
        } else {
            //非会员卡消费
            //1.普通消费
            //2.卡券消费


            //查询积分流水表 - 根据order_pay订单编号
            MbrScoreStream mbrScoreStream = mbrScoreStreamRepository.fromPayOrderNumberAndType(event.getOrderPayNumber(), MbrScoreType.CONSUMER);
            if(null != mbrScoreStream){
                // 已经处理过
                return false;
            }
        }
        return true;
    }


    private String lock(MbrPaySuccessEvent event) {
        String lockKey ;
        if(Objects.equals(PayEntry.MSCARDPAY,event.getPayEntry())){
            lockKey = CONSUMER_LOCK_PRE + event.getMbrOrderId() + event.serviceType();
        } else {
            lockKey = CONSUMER_LOCK_PRE + event.getOrderPayNumber() + event.serviceType();
        }
        //获取锁
        if(!memberRedisDomainService.lock(lockKey, String.valueOf(System.currentTimeMillis()),5,TimeUnit.MINUTES)){
            log.info("处理消费业务获取锁失败:{}",event.toString());
            return null;
        }
        return lockKey;
    }

//    /**
//     * 支付即会员
//     * @param event
//     */
//    private Member paymentIsMember(MbrPaySuccessEvent event) {
//        OperationInfo info = new OperationInfo(
//                new MerchantId(event.getMerchantId()),
//                null == event.getStoreId() ? null : new StoreId(event.getStoreId()),
//                null == event.getStoreUserId() ? null : new StoreUserId(event.getStoreUserId()),
//                null == event.getPayTerminal() ? null : MbrSourceTerminal.fromSourceTerminal((byte) event.getPayTerminal().code),
//                "",
//                null == event.getMerchantUserId() ? null : new MerchantUserId(event.getMerchantUserId())
//        );
//        return memberDomainService.registerMemberWithOpenid(info,event.getOpenId());
//    }



    /**
     * 记录储值流水
     * 账户储值变动,新增储值流水,发送消息
     */
    private MbrStoredStream recordMbrStoredStream(Member member, MbrAccount mbrAccount, MbrOrder mbrOrder, MbrPaySuccessEvent event) {

        final OperatorInfo operatorInfo = event.getOperatorInfo();
        //账户变动
        mbrAccount.changeMoney(mbrOrder.getPaidAmount(),MbrStoredType.CONSUMER);
        //新增储值流水记录
        MbrStoredStream mbrStoredStream = new MbrStoredStream(
                member.getId(),
                mbrOrder.getPaidAmount(),
                mbrAccount.getAvailableBalance(),
                MbrStoredType.CONSUMER,
                null,
                "消费",
                mbrOrder.getId(),
                null,
                operatorInfo.getMerchantUserId(),
                operatorInfo.getStoreUserId(),
                operatorInfo.getStoreId()
        );
        mbrStoredStreamRepository.save(mbrStoredStream);

        if(mbrOrder.getPaidAmount().compareTo(BigDecimal.ZERO) > 0){
            //消息推送
            memberMsgDomainService.sendMbrCardConsumerMsg(member, mbrAccount, mbrStoredStream,
                    () -> operatorInfo.getOperatorAddress());
        }
        return mbrStoredStream;
    }

    /**
     * 记录积分流水
     * @param event
     * @param member
     * @param mbrAccount
     * @param mbrStoredStream
     * @param score
     */
    private MbrScoreStream recordMbrScoreStream(MbrPaySuccessEvent event, Member member, MbrAccount mbrAccount, MbrStoredStream mbrStoredStream, Long score) {

        if(0 >= score){
            return null;
        }
        String remark = "消费"+event.getRealPayAmount()+"元赠送"+score+"积分";
        OperatorInfo operatorInfo = event.getOperatorInfo();
        mbrAccount.changeScore(score,MbrScoreType.CONSUMER);
        MbrScoreStream mbrScoreStream = new MbrScoreStream(
                member.getId(),
                score,
                MbrScoreType.CONSUMER,
                null == mbrStoredStream ? null : mbrStoredStream.getId(),
                null,
                null,
                event.getOrderPayNumber(),
                mbrAccount.getAvailableScore(),
                remark,
                event.getPayEntry(),
                operatorInfo.getMerchantUserId(),
                operatorInfo.getStoreUserId(),
                operatorInfo.getStoreId()
        );
        mbrScoreStreamRepository.save(mbrScoreStream);
        //消息推送
        memberMsgDomainService.sendMbrScoreChangeMsg(member.getOperationInfo().getMerchantId().getId(), mbrAccount, mbrScoreStream,
                remark);
        return mbrScoreStream;
    }

    /**
     * 消费时使用优惠券
     * @param event
     */
    private void consumerUseCoupon(MbrPaySuccessEvent event) {
        if(!Objects.equals(PayEntry.MSCARDPAY,event.getPayEntry())){

        } else {

        }
    }
}
